/* SHSMODe -- Serial High Speed Mode enabler/patcher V1.7a */
/* Written by gigo@yk.rim.or.jp (imaizumi@nisiq.net) */
/* Special thanks for h-mio@mx5.nisiq.net */
/* V1.0f Modified for Linux by mizuhara@coup.rim.or.jp */
/* V1.6  Modified for Linux by ytakeuch@po.iijnet.or.jp */
/* compiler DOS:MSC6.00A, Linux:gcc */

/*
History:
  1.7a(29-Aug-98)
    compile with #define PC87338, without NOACPI.
  1.7(25-Aug-98)
    Support ALI M1543(Aladdin V south bridge)
  1.6(17-Aug-98)
    Support NS PC87307,97307,87317,87308,87309(,87338)
    Fix/Add/Del SMC Table.
    WinBond W83877TF support ?
    Fix Winbond patch.
    Linux/FreeBSD support.(by youji takeuchi)
  1.5(23-Jul-98)
    Support Winbond 87977F/AF/TF/ATF
    Support Windows98
    Support 37N769,37M60x/61x/70x,37B77x/78x,37N958FR
    Added patch/depatch function
  1.3-1.4
    experimental,not released.
  1.2b(25-Dec-96)
    Added support for 67x,68x
  1.2a(01-Nov-96)
    Add 'B'(oth) port option.
  1.2(31-Oct-96)
    Add support for 669FR,93xFR,93xAPM,957FR(Not tested yet.)
    Display port address for convenience.
  1.0f
    add debug code & adaptation for linux.
  1.0d, 1.0e
    experimental,not released.
  1.0b -> 1.0c
    display string "com" -> "port", function exactly same.

Known limitation for linux:
    Perhaps, this version fail to detect/set operation rare.
    You must check result.
    In hsmode,you can use ordinary setserial command to over 115kbps.

    For SMC
        230kbps: setserial <dev> spd_cust divisor 0x8002
        460kbps: setserial <dev> spd_cust divisor 0x8001
    For NS/WinBond
        230kbps: setserial <dev> spd_cust divisor 0x4
        460kbps: setserial <dev> spd_cust divisor 0x2
        921kbps: setserial <dev> spd_cust divisor 0x1
*/
#define PATCH
#define PC87338
/*
#define CHKENB
*/

#if defined(unix)
/* disable patch function */
#undef PATCH
#endif

#include <stdio.h>
#include <stdlib.h>
#if defined(unix)
void port_init(void);
void port_finish(void);
#define _disable() port_init()
#define _enable() port_finish()
#if defined(linux)
int inp(unsigned);
void outp(unsigned, unsigned char);
#elif defined(__FreeBSD__)
#include <machine/cpufunc.h>
#define inp(port)   inb(port)
#define outp(port, val) outb(port, val)
#endif /* !defined(linux) && defined(__FreeBSD__) */
#else /* !defined(unix) */
#include <conio.h>
#include <io.h>
#include <dos.h>
#endif /* !defined(unix) */
#include <string.h>

/* functions */
void usage(void);
int chip(int n, char opr);              /* chip detect */
void ope(int n, char opr, int portNo);  /* option D,H,L operation */
void srdump(int n,int start,int end);   /* register dump for debug */

#if defined(PATCH)
int dopatch(int dist,char opr);         /* patch/depatch */
long search(long fsize, int n, unsigned char *code);    /* search pattern in serail.vxd */
int check_drv(char opr);                /* check applied patch */
void chg_autoexec(char opr,char *path); /* process autoexec.bat */
#endif

typedef struct idn {
unsigned short port;
unsigned char ini1;
unsigned char ini2;
unsigned char idx;
unsigned char id;
unsigned char rev;
unsigned char fin;
int pch;
int type;
char *name;
} idn_t;

idn_t idnt[] = {
    { 0x3f0,0x55,0x55,0x20,0x4c,0x01,0xAA,1,4,"B72x"    },  /*00:B72x */
    { 0x370,0x55,0x55,0x20,0x4c,0x01,0xAA,1,4,"B72x"    },  /*01:B72x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"B77x"    },  /*02:B77x */
    { 0x370,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"B77x"    },  /*03:B77x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x44,0x01,0xAA,1,4,"B78x"    },  /*04:B78x */
    { 0x370,0x55,0x55,0x20,0x44,0x01,0xAA,1,4,"B78x"    },  /*05:B78x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"B80x"    },  /*06:B80x */
    { 0x370,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"B80x"    },  /*07:B80x at 0x370 */
    { 0x3f0,0x55,0x55,0x0d,0x65,0x02,0xAA,0,1,"C665GT"  },  /*08:C665GT */
    { 0x3f0,0x44,0x44,0x0d,0x66,0x02,0xAA,0,1,"C666GT"  },  /*09:C666GT */
    { 0x3f0,0x55,0x55,0x0d,0x65,0x82,0xAA,1,2,"C665IR"  },  /*10:C665IR */
    { 0x3f0,0x44,0x44,0x0d,0x66,0x82,0xAA,1,2,"C666IR"  },  /*11:C666IR */
    { 0x3f0,0x55,0x55,0x0d,0x03,0x00,0xAA,1,3,"C669"    },  /*12:C669 */
    { 0x370,0x55,0x55,0x0d,0x03,0x00,0xAA,1,3,"C669"    },  /*13:C669 at 0x370 */
    { 0x3f0,0x55,0x55,0x0d,0x04,0x00,0xAA,1,3,"C669FR"  },  /*14:C669FR */
    { 0x370,0x55,0x55,0x0d,0x04,0x00,0xAA,1,3,"C669FR"  },  /*15:C669FR at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x40,0x01,0xAA,1,4,"C67x"    },  /*16:C67x */
    { 0x370,0x55,0x55,0x20,0x40,0x01,0xAA,1,4,"C67x"    },  /*17:C67x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x48,0x01,0xAA,1,4,"C68x"    },  /*18:C68x */
    { 0x370,0x55,0x55,0x20,0x48,0x01,0xAA,1,4,"C68x"    },  /*19:C68x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x02,0x01,0xAA,1,4,"C93x"    },  /*20:C93x */
    { 0x370,0x55,0x55,0x20,0x02,0x01,0xAA,1,4,"C93x"    },  /*21:C93x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x30,0x01,0xAA,1,4,"C93xAPM" },  /*22:C93xAPM */
    { 0x370,0x55,0x55,0x20,0x30,0x01,0xAA,1,4,"C93xAPM" },  /*23:C93xAPM at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x03,0x01,0xAA,1,4,"C93xFR"  },  /*24:C93xFR */
    { 0x370,0x55,0x55,0x20,0x03,0x01,0xAA,1,4,"C93xFR"  },  /*25:C93xFR at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x47,0x01,0xAA,1,4,"M60x"    },  /*26:M60x */
    { 0x370,0x55,0x55,0x20,0x47,0x01,0xAA,1,4,"M60x"    },  /*27:M60x at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x46,0x01,0xAA,1,4,"M61x"    },  /*28:M61x */
    { 0x370,0x55,0x55,0x20,0x46,0x01,0xAA,1,4,"M61x"    },  /*29:M61x at 0x370 */
    { 0x3f0,0x55,0x55,0x0d,0x28,0x00,0xAA,1,3,"N769"    },  /*30:N769FR */
    { 0x370,0x55,0x55,0x0d,0x28,0x00,0xAA,1,3,"N769"    },  /*31:N769FR at 0x370 */
    { 0x3f0,0x55,0x55,0x20,0x09,0x01,0xAA,1,4,"N958FR"  },  /*32:N958FR */
    { 0x370,0x55,0x55,0x20,0x09,0x01,0xAA,1,4,"N958FR"  },  /*33:N958FR at 0x370 */

#if 0
    /* deleted ? */
    { 0x3f0,0x55,0x55,0x20,0x07,0x01,0xAA,1,4,"C957FR"  },  /*34:C957FR */
    { 0x370,0x55,0x55,0x20,0x07,0x01,0xAA,1,4,"C957FR"  },  /*35:C957FR at 0x370 */
    /* same ID as B80 ?? */
    { 0x3f0,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"M70x"    },  /*26:M70x */
    { 0x370,0x55,0x55,0x20,0x42,0x01,0xAA,1,4,"M70x"    },  /*27:M70x at 0x370 */
    /* deleted ? */
    { 0x3f0,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"M77x"    },  /*28:M77x */
    { 0x370,0x55,0x55,0x20,0x43,0x01,0xAA,1,4,"M77x"    },  /*29:M77x at 0x370 */
#endif
#if 0
    /* no function but detectable */
    { 0x250,0x89,0x89,0x09,0x0a,0x01,0xAA,0,6,"W83877F"},   /*--:W83877F at 0x250 */
    { 0x250,0x88,0x88,0x09,0x0a,0x01,0xAA,0,6,"W83877F"},   /*--:W83877F at 0x250 */
    { 0x3F0,0x87,0x87,0x09,0x0a,0x01,0xAA,0,7,"W83877F"},   /*--:W83877F at 0x3f0 */
    { 0x3F0,0x86,0x86,0x09,0x0a,0x01,0xAA,0,7,"W83877F"},   /*--:W83877F at 0x3f0 */
#endif
    { 0x250,0x89,0x89,0x09,0x0c,0x01,0xAA,2,8,"W83877TF"},  /*34:W83877TF at 0x250 */
    { 0x250,0x88,0x88,0x09,0x0c,0x01,0xAA,2,8,"W83877TF"},  /*35:W83877TF at 0x250 */
    { 0x3F0,0x87,0x87,0x09,0x0c,0x01,0xAA,2,9,"W83877TF"},  /*36:W83877TF at 0x3f0 */
    { 0x3F0,0x86,0x86,0x09,0x0c,0x01,0xAA,2,9,"W83877TF"},  /*37:W83877TF at 0x3f0 */

    { 0x3f0,0x87,0x87,0x20,0x97,0x71,0xAA,2,10,"W83977" },  /*38:W83977A/AF/TF/ATF at 0x3f0 */
    { 0x370,0x87,0x87,0x20,0x97,0x71,0xAA,2,10,"W83977" },  /*39:W83977A/AF/TF/ATF at 0x370 */
#if 0
    /* no function but detectable */
    { 0x02e,0x00,0x00,0x08,0x90,0x00,0xAA,3,01,"87336"  },  /*--:PC87336 at 0x02e */
    { 0x15c,0x00,0x00,0x08,0x90,0x00,0xAA,3,01,"87336"  },  /*--:PC87336 at 0x15c */
    { 0x398,0x00,0x00,0x08,0x90,0x00,0xAA,3,01,"87336"  },  /*--:PC87336 at 0x398 */
#endif
    { 0x02e,0x00,0x00,0x20,0xa0,0x00,0xAA,3,20,"87308"  },  /*40:PC87308 at 0x02e */
    { 0x15c,0x00,0x00,0x20,0xa0,0x00,0xAA,3,20,"87308"  },  /*41:PC87308 at 0x15c */
#if defined(PC87338)
    { 0x02e,0x00,0x00,0x08,0xb0,0x00,0xAA,3,28,"87338"  },  /*42:PC87338 at 0x02e */
    { 0x15c,0x00,0x00,0x08,0xb0,0x00,0xAA,3,28,"87338"  },  /*43:PC87338 at 0x15c */
    { 0x398,0x00,0x00,0x08,0xb0,0x00,0xAA,3,28,"87338"  },  /*44:PC87338 at 0x398 */
#endif
    { 0x02e,0x00,0x00,0x20,0xc0,0x00,0xAA,3,22,"87307"  },  /*45:PC87307 at 0x02e */
    { 0x15c,0x00,0x00,0x20,0xc0,0x00,0xAA,3,22,"87307"  },  /*46:PC87307 at 0x15c */
    { 0x02e,0x00,0x00,0x20,0xcf,0x00,0xAA,3,21,"97307"  },  /*47:PC97307 at 0x02e */
    { 0x15c,0x00,0x00,0x20,0xcf,0x00,0xAA,3,21,"97307"  },  /*48:PC97307 at 0x15c */
    { 0x02e,0x00,0x00,0x20,0xd0,0x00,0xAA,3,22,"87317"  },  /*49:PC87317 at 0x02e */
    { 0x15c,0x00,0x00,0x20,0xd0,0x00,0xAA,3,22,"87317"  },  /*50:PC87317 at 0x15c */
    { 0x02e,0x00,0x00,0x20,0xdf,0x00,0xAA,3,21,"97317"  },  /*51:PC87317 at 0x02e */
    { 0x15c,0x00,0x00,0x20,0xdf,0x00,0xAA,3,21,"97317"  },  /*52:PC87317 at 0x15c */
    { 0x02e,0x00,0x00,0x20,0xe0,0x00,0xAA,3,23,"87309"  },  /*53:PC87309 at 0x02e */
    { 0x15c,0x00,0x00,0x20,0xe0,0x00,0xAA,3,23,"87309"  },  /*54:PC87309 at 0x15c */
    { 0x3f0,0x51,0x23,0x20,0x43,0x15,0xBB,4,30,"M1543"  },  /*55:M1543 at 0x3f0 */
    { 0x370,0x51,0x23,0x20,0x43,0x15,0xBB,4,30,"M1543"  },  /*56:M1543 at 0x370 */
    {     0,   0,   0,   0,   0,   0,   0,0, 0,"Other"  }
};

#if defined(PATCH)

unsigned char org_code[] = {
    0x33,0xC0,
    0x81,0xF9,0x00,0xC2,0x01,0x00,0x74,0x0D,
    0x81,0xF9,0x10,0xFF,0x00,0x00,0x73,0x17,
    0x83,0xF9,0x02,0x72,0x09,0x33,0xD2,0xB8,
    0x00,0xC2,0x01,0x00,0xF7,0xF1,0x8B,0xC8,
    0xB8,0xF4,0xFF,0xFF,0xFF,0x5A,0xC3,0x81,
    0xF9,0x1F,0xFF,0x00,0x00,0x77,0xE6,0x53,
    0x8B,0xD9,0x81,0xEB,0x10,0xFF,0x00,0x00,
    0xD1,0xE3,0x0F,0xB7,0x83,0xA8,0x11,0x00,
    0x00,0x5B,0xEB,0xDA
};

unsigned char smc_code[] = {
    0x33,0xC0,
    0x8B,0xD1,0x81,0xEA,0x10,0xFF,0x00,0x00,
    0x76,0x05,0x83,0xFA,0x0F,0x72,0x29,0x83,
    0xF9,0x02,0x72,0x1B,0x33,0xD2,0xB8,0x00,
    0x08,0x07,0x00,0xF7,0xF1,0x8B,0xD0,0x4A,
    0x74,0x03,0x4A,0x75,0x07,0x0D,0x00,0x80,
    0x00,0x00,0xEB,0x03,0xC1,0xE8,0x02,0x8B,
    0xC8,0xB8,0xF4,0xFF,0xFF,0xFF,0x5A,0xC3,
    0xD1,0xE2,0x0F,0xB7,0x82,0xA8,0x11,0x00,
    0x00,0x90,0xEB,0xEB
};

#define SMC_LEN0 63
#define TBL_ADR_LEN 4
#define SMC_LEN1 3
#define SMC_LEN 70

#define DVS1_OFS 2
#define DVS2_OFS 24
#define DVS 921600
static unsigned char wb_code[] = {
    0x33,0xC0,
    0x81,0xF9,0x00,0x10,0x0E,0x00,0x74,0x0D,
    0x81,0xF9,0x10,0xFF,0x00,0x00,0x73,0x17,
    0x83,0xF9,0x02,0x72,0x09,0x33,0xD2,0xB8,
    0x00,0x10,0x0E,0x00,0xF7,0xF1,0x8B,0xC8,
    0xB8,0xF4,0xFF,0xFF,0xFF,0x5A,0xC3,0x81,
    0xF9,0x1F,0xFF,0x00,0x00,0x77,0xE6,0x53,
    0x8B,0xD9,0x81,0xEB,0x10,0xFF,0x00,0x00,
    0xD1,0xE3,0x0F,0xB7,0x83,0xA8,0x11,0x00,
    0x00,0x5B,0xEB,0xDA
};

static unsigned char ns_code[] = {
    0x83,0xF8,0xFB, /*  cmp eax,IE_Default  ; bad coding.. sorry.*/
    0x74,0x0F,      /*  jz  SetComChk       ; check only -> */
                    /*                      ; (edx points LCR) */
    0x90,           /*  nop */
    0x90,           /*  nop */

    0xB0,0xE0,      /*  mov al,0e0h         ; select bank2 */
    0xEE,           /*  out dx,al           ; */
    0x42,           /*  inc edx             ; set EXCR2 */
    0xEC,           /*  in  al,dx           ; */
    0x24,0xCF,      /*  and al,0CFh         ; mask PRESL1,PRESL0 */
    0x0C,0x10,      /*  or  al,010h         ; divisor = 1.625 */
    0xEE,           /*  out dx,al           ; */
    0x90,           /*  nop */
    0x90,           /*  nop */
    0x90,           /*  nop */
                    /*SetComChk:            ; */
    0x33,0xC0,      /*  xor eax,eax         ; assume error */
    0x8B,0xD1,      /*  mov edx,ecx         ; */
    0x81,0xEA,      /*  sub edx,CBR_110     ; */
     0x10,0xFF,0x00,0x00,
    0x76,0x05,      /*  jna SetCom921600    ; < CBR_110 ? -> Not Index */
    0x83,0xFA,0x0F, /*  cmp edx,CBR_56000-CBR_110 */
    0x72,0x15,      /*  jnae by_index       ; ? -> Index */
                    /*SetCom921600:         ; */
    0x83,0xF9,0x02, /*  cmp ecx,2           ; */
    0x72,0x09,      /*  jnae SetCom310      ; Error -> */
    0x33,0xD2,      /*  xor edx,edx         ; EDX:EAX = 921,600 */
    0xB8,0x00,0x10,0x0E,0x00,   /*  mov eax,921600 */
    0xF7,0xF1,      /*  div ecx             ; (EAX) = 921,600/baud */
                    /*SetCom310:            ; */
    0x8B,0xC8,      /*  mov ecx,eax         ;(ECX) = baud rate, or error code (0) */
    0x83,0xE8,0x0C, /*  sub eax,0-IE_BAUDRATE ;set error code incase bad baud */
    0x5A,           /*  pop edx             ; */
    0xC3,           /*  ret                 ; */
                    /*by_index:             ; */
    0xD1,0xE2,      /*  shl edx,1           ; */
    0x0F,0xB7,0x82, /*  movzx   eax,WORD PTR [edx+BaudRateByIndexTable] ; get divisor */
    0xA8,0x11,0x00,0x00,/* 000000FC R ; !! don't move !! */
    0x90,           /*  nop */
    0xEB,0xED       /*jmp SetCom310         ; return */
};

static unsigned char x1_tbl[] = {
    0x17,0x04,0x80,0x01,0xC0,0x00,0x60,0x00,
    0x30,0x00,0x18,0x00,0x0C,0x00,0x08,0x00,
    0x06,0x00,0x00,0x00,0x00,0x00,0x03,0x00
};

static unsigned char x8_tbl[] = {
    0xC7,0x20,0x04,0x0c,0x02,0x06,0x01,0x03,
    0x80,0x01,0x60,0x00,0x0C,0x00,0x40,0x00,
    0x30,0x00,0x00,0x00,0x00,0x00,0x18,0x00
};

typedef struct pch {
    unsigned char* code;
    int len;
    char *name;
} pch_t;

pch_t patch[] = {
    {org_code, SMC_LEN0, "Original"},
    {smc_code, SMC_LEN0, "SMC/ALI Patch"},
    {wb_code , SMC_LEN0, "Winbond Patch"},
    {ns_code , SMC_LEN0, "NS Patch"},
    {NULL    , 0      , "Unknown"},
    {NULL    , 0      , "Old patch"}
};

FILE *fd;
char fname[128];
char sysdir[128]="c:\\windows\\system";
char *windir;
int  drv_type;
long adr1,adr2;
char buf[256];
#endif

int divisor;

#if defined(DEBUG)
#define outp(p,v) outNT(p,v)
#define inp(p) inpNT(p)
void outNT(unsigned port, int val) {
    printf("out 0x%04x, 0x%02x\n",port,val);
}
int inpNT(unsigned port) {
    char buf[256];
    unsigned c;
    printf("inp(0x%04x) = \?",port);
    scanf("%s",buf);
    sscanf(buf,"%x",&c);
    return c;
/*
    printf("inp(0x%04x) \n",port);
    return 0xff;
*/
}
#endif

void main(int argc, char *argv[])
{
    char opr,*fab;
    int n,portNo,force,detect;

    fputs("shsmod V1.7a (29-Aug-1998)\n",stdout);

    n = 0; portNo = 0; opr = 'D';  /* display only */
    detect = -1; force = -1;

#if !defined(unix)
    if (strcmp(getenv("OS"),"Windows_NT") == 0) {
#if defined(DEBUG)
        fprintf(stderr,"Windows NT, Debug only!\n");
#else
        fprintf(stderr,"You must run this on DOS/Windows95/Windows98 !\n");
        exit(1);
#endif
    } else if ((windir = getenv("windir")) != NULL) {
        strcpy(sysdir,windir);
        strcat(sysdir,"\\system");
    }
#endif

    if (argc > 1) {
        switch (argv[1][0]) {
        case '0':
            break;
        case '1':
            portNo = 0x40;
            break;
        case '2':
            portNo = 0x80;
            break;
        case '3':
        case 'b':
        case 'B':
            portNo = 0xC0;
            break;
#if defined(PATCH)
        case 'p':
        case 'P':
            if (stricmp(argv[1],"patch") == 0) {
                opr = 'P';
                break;
            }
            /* no break for syntax error */
        case 'd':
        case 'D':
            if (stricmp(argv[1],"depatch") == 0) {
                opr = 'R';
                break;
            }
            /* no break for syntax error */
#endif
        default:
            usage();
            break;
        }
    }
    divisor = -1;
    if (argc > 2){
        if (opr != 'D') {
#if defined(PATCH)
            strcpy(sysdir,argv[2]);
#endif
        } else {
            opr = (char)(toupper(argv[2][0]));
            switch (opr) {
            case 'D':
                portNo = 0;
                /* no break; */
            case 'H':
            case 'L':
            case 'C':
                break;
            default:
                { long l;
                    l = atol(argv[2]);
                    if (l > 0) {
                        if ((l<110)|| (l > 921600) || (921600%l))
                            usage();
                        divisor = (unsigned) (921600L/l);
                    }else if (l==0)
                        divisor = 0;
                    opr = 'H';
                }
            }
        }
    }

#if defined(PATCH)
    strcpy(fname,sysdir);
    strcat(fname,"\\serial.vxd");
#endif

    if (argc > 3) {
        force = atoi(argv[3]);
        for (n=0; idnt[n].type && n!=force; n++);
        force = n;
        printf("***Force %s***\n",idnt[force].name);
    }

    for (; idnt[n].type != 0; n++) {
        if ((n == force) || (chip(n,opr) != -1)) {
            detect = n;
            break;
        }
    }
    if (detect == -1) {
        fputs("!!! Not ALI M1543(Aladdin IV+,V south bridge) chip !!!\n",stdout);
#if defined(PC87338)
        fputs("!!! Not NS PC87307/87308/87309/87317/87338/97307/97317 chip !!!\n",stdout);
#else
        fputs("!!! Not NS PC87307/87308/87309/87317/97307/97317 chip !!!\n",stdout);
#endif
        fputs("!!! Not SMC37B72x/77x/78x/80x chip !!!\n",stdout);
        fputs("!!! Not SMC37C665IR/666IR/669/669FR/67x/68x/93x/93xAPM/93xFR chip !!!\n",stdout);
        fputs("!!! Not SMC37M60x/61x,37N769/958FR chip !!!\n",stdout);
        fputs("!!! Not Winbond W83877TF,W83977F/AF/TF/ATF chip !!!\n",stdout);
        exit(1);
    }

    switch(idnt[n].pch) {
    case 1:
        fab = "SMC37";
        break;
    case 2:
        fab = "Winbond ";
        break;
    case 3:
        fab = "NS PC";
        break;
    case 4:
        fab = "ALI ";
        break;
    default:
        fab = "nowhere ";
        break;
    }
    printf("%s%s at %04xh detected\n",fab,idnt[n].name,idnt[n].port);

    if ((opr == 'L') && (idnt[n].pch == 4)){
        printf("!!! SHSMOD can't return to Low with this chip.!!!\n");
        exit(1);
    }
    if ((divisor >= 0) && (idnt[n].pch != 4 )){
        printf("!!! SHSMOD can't set baud with this chip.!!!\n");
        exit(1);
    }

#if defined(PATCH)
    drv_type = check_drv(opr);
    switch(opr) {
    case 'P':   /* patch */
    case 'R':
        if (dopatch(idnt[n].pch,opr) > 0 ) {
            chg_autoexec(opr,argv[0]);
            fputs("\n*** Restart windows to take effect. ***\n",stderr);
        }
        break;
    case 'H':
        if (drv_type == 0) {/* Winbond & NS87308 */
            if ((idnt[n].pch==2)||(idnt[n].type==20)) {
                fputs("Warnning - driver mismatch, may unexpected effect occur.\n",stdout);
            }
        }
        /* no break; */
#else
    switch(opr) {
    case 'H':
#endif
    case 'L':
    case 'D':
        ope(n,opr,portNo);
        break;
    case 'C': /* debug */
        srdump(n,0,0x2F);
        break;
    default:
        break;
    }
    exit(0);
}

void usage(void)
{
    fputs("usage:shsmod [port# [h/l/d]]\n",stderr);
    fputs("      shsmod port# baud (NS chip only. except PC87308)\n",stderr);
#if defined(PATCH)
    fputs("      shsmod patch|depatch [sysdir]\n",stderr);
#endif
    exit(1);
}

#if defined(unix)
#if defined(linux)
#define PORT_DEV "/dev/port"
#elif defined(__FreeBSD__)
#define PORT_DEV "/dev/io"
#endif /* !defined(linux) && defined(__FreeBSD__) */

FILE *fp;

void port_init(void) {
    if ((fp = fopen(PORT_DEV, "r+")) == NULL) {
        perror("error in fopen " PORT_DEV);
        exit(2);
    }
}

void port_finish(void) {
    fclose(fp);
}

#if defined(linux)

void port_error(const char *msg) {
    perror(msg);
    fclose(fp);
    exit(2);
}

int inp(unsigned port) {
    unsigned char buf[1];

#if 0
    fprintf(stderr, "inp: port = 0x%x\n", port);
#endif /* DEBUG */
    if (fseek(fp, port, SEEK_SET)) {
        port_error("error in fseek " PORT_DEV);
    }
    if (!fread(buf, 1, 1, fp)) {
        port_error("error in fread " PORT_DEV);
    }
    return buf[0];
}

void outp(unsigned port, unsigned char val) {
#if 0
    fprintf(stderr, "outp: port = 0x%x, val = 0x%x\n", port, val);
#endif /* DEBUG */
    if (fseek(fp, port, SEEK_SET)) {
        port_error("error in fseek " PORT_DEV);
    }
    if (!fwrite(&val, 1, 1, fp)) {
        port_error("error in fwrite " PORT_DEV);
    }
}

#endif /* defined(linux) */
#endif /* defined(unix) */

int chip(int n,char opr)
{
    unsigned char id,rev;
#if defined(DEBUG)
    printf("\n%s\n",idnt[n].name);
#endif
    _disable(); /* cli */
    /* write ini value to enter config mode */
    if (idnt[n].ini1)
        outp(idnt[n].port,idnt[n].ini1);
    if (idnt[n].ini2)
        outp(idnt[n].port,idnt[n].ini2);
    if (idnt[n].type != 8) {
        outp(idnt[n].port,idnt[n].idx);
        id = (unsigned char)inp(idnt[n].port+1);
    } else {
        outp(idnt[n].port+1,idnt[n].idx);
        id = (unsigned char)inp(idnt[n].port+2);
    }
    switch (idnt[n].type) {
    default:
        outp(idnt[n].port,idnt[n].idx+1);
        rev = (unsigned char)inp(idnt[n].port+1);
        break;
    case 8:
    case 9:
        id = (unsigned char)(id&0xf);
        rev = 0xff;
        break;
    case 20:
    case 22:
    case 28:
        id = (unsigned char) (id & ~7);
        rev = (unsigned char)(id &  7);
        break;
    case 21:
    case 23:
        rev = 0xff;
        break;
    }
    outp(idnt[n].port,idnt[n].fin); /* exit config mode */
    _enable();  /* sti */

    if (opr == 'C')
        printf("*Check %s: port=%04x, ini1=%02x, ini2=%02x, idx=%02x -> id=%02x, rev=%02x\n"
         ,idnt[n].name,idnt[n].port,idnt[n].ini1,idnt[n].ini2,idnt[n].idx,id,rev);
    /* id & (rev or not 665IR/666IR) ? */
    if ((idnt[n].id==id)&&((idnt[n].rev==rev)||(idnt[n].type!=2))&&(idnt[n].type>1))
        return n;
    return -1;
}

void ope(int n, char opr, int portNo)
{
    unsigned char r,v,cr1,cr2;
    int i,dvofs,nxt,portAdr[3];

    if (opr == 'P')
        return;

    switch(idnt[n].type) {
    case 0: /* Unknown */
    case 1: /* 665GT,666GT */
    default: /* internal error.. */
        break;
    case 2: /* 665IR,666IR */
        _disable(); /* cli */
        /* write ini value 2 times to enter config mode */
        outp(idnt[n].port,idnt[n].ini1);
        outp(idnt[n].port,idnt[n].ini2);
        outp(idnt[n].port,0x01);
        cr1 = (unsigned char) inp(idnt[n].port+1);
        outp(idnt[n].port,0x02);
        cr2 = (unsigned char) inp(idnt[n].port+1);
        /* select sr0c */
        outp(idnt[n].port,0x0c);
        r = (unsigned char) inp(idnt[n].port+1);
        if (opr == 'H')
            r |= portNo;
        if (opr == 'L')
            r &= ~portNo;
        outp(idnt[n].port+1,r);
        r = (unsigned char) inp(idnt[n].port+1);
        outp(idnt[n].port,idnt[n].fin); /* exit config mode */
        _enable();  /* sti */
        for (v=0x40,i=1;i<=2;++i) {
            printf("Port%d",i);
            if (cr2 & 0x4) {
                switch (cr2 & 0x3) {
                case 00: portAdr[i]=0x3F8;break;
                case 01: portAdr[i]=0x2F8;break;
                case 02:
                    switch(cr1 & 0x30){
                    case 0x00: portAdr[i]=0x338;break;
                    case 0x10: portAdr[i]=0x3E8;break;
                    case 0x20: portAdr[i]=0x2E8;break;
                    case 0x30: portAdr[i]=0x220;break;
                    }
                    break;
                case 03:
                    switch(cr1 & 0x30){
                    case 0x00: portAdr[i]=0x238;break;
                    case 0x10: portAdr[i]=0x2E8;break;
                    case 0x20: portAdr[i]=0x2E0;break;
                    case 0x30: portAdr[i]=0x228;break;
                    }
                    break;
                }
                printf("(%04xh) Serial Speed Mode: %s\n",portAdr[i],v&r ?"High":"Low" );
            } else {
                printf(" disabled\n");
            }
            cr2 >>= 4;
            v <<= 1;
        }
        break;
    case 3: /* 669 */
        _disable(); /* cli */
        /* write ini value 2 times to enter config mode */
        outp(idnt[n].port,idnt[n].ini1);
        outp(idnt[n].port,idnt[n].ini2);
        outp(idnt[n].port,0x24);
        portAdr[1] =  (inp(idnt[n].port+1) & 0xFE) << 2;
        outp(idnt[n].port,0x25);
        portAdr[2] =  (inp(idnt[n].port+1) & 0xFE) << 2;
        /* select sr0c */
        outp(idnt[n].port,0x0c);
        r = (unsigned char) inp(idnt[n].port+1);
        if (opr == 'H')
            r |= portNo;
        if (opr == 'L')
            r &= ~portNo;
        outp(idnt[n].port+1,r);
        r = (unsigned char) inp(idnt[n].port+1);
        outp(idnt[n].port,idnt[n].fin); /* exit config mode */
        _enable();  /* sti */
        for (v=0x40,i=1;i<=2;++i) {
            printf("Port%d",i);
            if (portAdr[i] & 0x300)
                printf("(%04xh) Serial Speed Mode: %s\n",portAdr[i],v&r ?"High":"Low" );
            else
                printf(" disabled\n");
            v <<= 1;
        }
        break;
    case 8: /* W83877TF native */
    case 9: /* W83877TF emulate mode */
        _disable(); /* cli */
        /* write ini value 2 times to enter config mode */
        outp(idnt[n].port,idnt[n].ini1);
        if (idnt[n].type != 8)
            outp(idnt[n].port,idnt[n].ini2);
        else
            idnt[n].port++;
        outp(idnt[n].port,0x24);
        portAdr[1] =  (inp(idnt[n].port+1) & 0xFE) << 2;
        outp(idnt[n].port,0x25);
        portAdr[2] =  (inp(idnt[n].port+1) & 0xFE) << 2;
        /* select sr19 */
        outp(idnt[n].port,0x19);
        r = (unsigned char) inp(idnt[n].port+1);
        v = 0;
        if (portNo & 0x40)
            v = 0x2;
        if (portNo & 0x80)
            v |= 0x1;
        if (opr == 'H')
            r |= v;
        if (opr == 'L')
            r &= ~v;
        outp(idnt[n].port+1,r);
        r = (unsigned char) inp(idnt[n].port+1);
        outp(idnt[n].port,idnt[n].fin); /* exit config mode */
        _enable();  /* sti */
        for (v=0x02,i=1;i<=2;++i) {
            printf("Port%d",i);
            if (portAdr[i] & 0x300)
                printf("(%04xh) Serial Speed Mode: %s\n",portAdr[i],v&r ?"High":"Low" );
            else
                printf(" disabled\n");
            v >>= 1;
        }
        if (idnt[n].type == 8)
            idnt[n].port--;
        break;
    case  4: /* 67x,68x,93x,957 dev 4,5*/
    case 30: /* ALI M1543 dev 4,5*/
    case 10: /* W83977TF dev 2,3*/
    case 20: /* NS PC8xxxxx dev 6,5*/
    case 21: /* NS PC9xxxxx dev 6,5*/
    case 22: /* NS PC8xxxxx dev 6,5*/
    case 23: /* NS PC9xxxxx dev 3,2*/
        switch(idnt[n].type) {
        case 20:
        case 21:
        case 22:
            dvofs = 6;  /* 5:port2, 6:port1 87307/97037/87308/87317/97317 */
            nxt = -1;
            break;
        case 23:
            dvofs = 3;  /* 2:port2, 3:port1 87309 */
            nxt = -1;
            break;
        case 10:    /* WinBond */
            dvofs = 2;  /* 2:port1, 3:port2 */
            nxt = 1;
            break;
        default:    /* SMC,ALI */
            dvofs = 4;  /* 4:port1, 5:port2 */
            nxt = 1;
            break;
        }

        for (i = 1; i <=2 ; ++i, dvofs += nxt) {
            _disable(); /* cli */
            /* write ini value 2 times to enter config mode */
            if (idnt[n].ini1)
                outp(idnt[n].port,idnt[n].ini1);
            if (idnt[n].ini2)
                outp(idnt[n].port,idnt[n].ini2);
            outp(idnt[n].port,0x07);/* Select device register */
            outp(idnt[n].port+1,dvofs); /* Set device value */
            outp(idnt[n].port,0x60);        /* get port addr. */
            portAdr[i] = inp(idnt[n].port+1) << 8;
            outp(idnt[n].port,0x61);
            portAdr[i] |= inp(idnt[n].port+1);
#if defined(CHKENB)
            outp(idnt[n].port,0x30);        /* enabled ? */
            if (inp(idnt[n].port+1)==0)
                portAdr[i] = 0;
#endif
            outp(idnt[n].port,0xF0);/* Select serial config register */
            r = (unsigned char)inp(idnt[n].port+1);
            switch (idnt[n].type) {
            case  4: /* SMC */
            case 30: /* ALI */
                if ((0x20 << i) & portNo) {
                    if (opr == 'H')
                        r |= 2;
                    if (opr == 'L')
                        r &= ~2;
                    outp(idnt[n].port+1,r);
                }
                r = (unsigned char)inp(idnt[n].port+1);
                v = (unsigned char)((r&0x3) == 0x2);
                break;
            case 10: /* Winbond 83977TF */
                if ((0x20 << i) & portNo) {
                    if (opr == 'H')
                        r |= 3;
                    if (opr == 'L')
                        r &= ~3;
                    outp(idnt[n].port+1,r);
                }
                r = (unsigned char)inp(idnt[n].port+1);
                v = (unsigned char)((r&0x3) == 0x3);
                break;
            case 20: /* NS 87308 */
#if 0
                if ((0x20 << i) & portNo)
                    if (opr == 'H')
                        outp(idnt[n].port+1,r|0x80);    /* bank select enable */
                v = (unsigned char) (inp(idnt[n].port+1) & 0x80);
                break;
#endif
            case 21: /* NS */
            case 22: /* NS */
            case 23: /* NS */
                if ((0x20 << i) & portNo) {
                    if (opr == 'H')
                        outp(idnt[n].port+1,r|0x80);    /* bank select enable */
                    if (divisor>=0) {
                        v = (unsigned char)inp(portAdr[i] + 0x03); /* save LCR for 83708 */
                        outp(portAdr[i] + 0x03 ,0xe0 ); /* Select Bank2 */
/*                      outp(portAdr[i] + 0x02 ,0x41 ); *//* Select Extended mode */
                        r = (unsigned char) inp(portAdr[i] + 0x04);
                        if (divisor==0)
                            outp(portAdr[i] + 0x04,r & 0x7F);/* clear LOCK */
                        else {
                            outp(portAdr[i] + 0x04,r | 0x90);/* LOCK,1.625 */
                            outp(portAdr[i] , divisor );
                            outp(portAdr[i] + 1, divisor >> 8 );
                        }
                        outp(portAdr[i] + 0x03 , v); /* Restore LCR */
                    }
                }
                v = (unsigned char)(inp(idnt[n].port+1)&0x80);
                break;
            default:
                break;
            }
            outp(idnt[n].port,idnt[n].fin); /* exit config mode */
            _enable();  /* sti */
            if (opr == 'C')
                printf("*Check srf0/EXCR2: %02x -- ",r);
            printf("Port%d",i);
            if (portAdr[i])
                printf("(%04xh) Serial Speed Mode: %s\n",portAdr[i],v ?"High":"Low" );
            else
                printf(" disabled\n");
        }
        break;
#if defined(PC87338)
    case 28: /* PC87338 */
        outp(idnt[n].port,0x00);    /* get FER 3:FDC 2:SCC2 1:SCC1 0:PPA */
        cr1 = (unsigned char) inp(idnt[n].port+1);
        outp(idnt[n].port,0x01);    /* get FAR 7:6-COM34 5:4-SCC2 3:2-SCC1 2:1-PPA */
        cr2 = (unsigned char) inp(idnt[n].port+1);

        for (i=1;i<=2;++i) {
            printf("Port%d",i);
            if ((cr1 & (1 << i) )==0) {
                printf(" disabled\n");
                continue;
            }
            switch ((cr2>>(2*i)) & 0x03) {
            case 00: portAdr[i]=0x3F8;break;
            case 01: portAdr[i]=0x2F8;break;
            case 02: /* com3 */
                switch(cr2 & 0xc0){
                case 0x00: portAdr[i]=0x3E8;break;
                case 0x40: portAdr[i]=0x338;break;
                case 0x80: portAdr[i]=0x2E8;break;
                case 0xc0: portAdr[i]=0x220;break;
                }
                break;
            case 03: /* com4 */
                switch(cr2 & 0xc0){
                case 0x00: portAdr[i]=0x2E8;break;
                case 0x40: portAdr[i]=0x238;break;
                case 0x80: portAdr[i]=0x2E0;break;
                case 0xc0: portAdr[i]=0x228;break;
                }
                break;
            }
            if ((0x20 << i) & portNo) {
                if (opr == 'H') {
                    outp(idnt[n].port,0x40);    /* enable bank select */
                    outp(idnt[n].port+1,inp(idnt[n].port+1)| (0x10 << (3*(i-1))) );
                }
                if (divisor>=0) {
                    v = (unsigned char)inp(portAdr[i] + 0x03); /* save LCR for 83708 */
                    outp(portAdr[i] + 0x03 ,0xe0 ); /* Select Bank2 */
/*                  outp(portAdr[i] + 0x02 ,0x41 ); *//* Select Extended mode */
                    r = (unsigned char) inp(portAdr[i] + 0x04);
                    if (divisor==0)
                        outp(portAdr[i] + 0x04,r & 0x7F);/* clear LOCK */
                    else {
                        outp(portAdr[i] + 0x04,r | 0x90);/* LOCK,1.625 */
                        outp(portAdr[i] , divisor );
                        outp(portAdr[i] + 1, divisor >> 8 );
                    }
                    outp(portAdr[i] + 0x03 , v); /* Restore LCR */
                }
            }
            v = (unsigned char)(inp(idnt[n].port+1)& (0x10 << (3*(i-1))) );
            if (opr == 'C')
                printf("*Check EXCR2: %02x -- ",r);
            printf("(%04xh) Serial Speed Mode: %s\n",portAdr[i],v ?"High":"Low" );
        }
        break;
#endif
    }
}

void srdump(int n,int start,int end)
{
    int i;
    unsigned char buf[256], *bp;

    if ((end-start) > 255) {
        fprintf(stderr,"Too big to dump\n");
        return;
    }
    printf("   +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F");

    bp = &buf[0];
    _disable(); /* cli */
    /* write ini value 2 times to enter config mode */
    if (idnt[n].ini1)
        outp(idnt[n].port,idnt[n].ini1);
    if (idnt[n].ini2)
        outp(idnt[n].port,idnt[n].ini2);
    if (idnt[n].type == 8)
        idnt[n].port++;
    for (i=start;i<=end;++i) {
        outp(idnt[n].port,i);
        *bp++ = (unsigned char)inp(idnt[n].port+1);
    }
    if (idnt[n].type == 8)
        idnt[n].port--;
    outp(idnt[n].port,idnt[n].fin); /* exit config mode */
    _enable();  /* sti */
    bp = &buf[0];
    for (i=start;i<=end;++i) {
        if (((i-start)%16)==0)
            printf("\n%02x:",i);
        printf("%02x ",*bp++);
    }
    printf("\n");
}

#if defined(PATCH)
int dopatch(int dist,char opr)
{
    int i,source;

    source = 0;
    if (dist == 4)  /* ALI patch is same as SMC */
        dist = 1;

    if (opr == 'R') {   /* depatch */
        i = source;
        source = dist;
        dist = i;
    }
    if (drv_type == dist) {
        fputs("\nNo need to patch/depatch process.\n\n",stderr);
        return 0;
    }
    if (dist == 0) { /* backup exist, (if not do real depatch) */
        strcpy(buf,"copy ");
        strcat(buf,sysdir);
        strcat(buf,"\\$erial.vxd");
        if (access(&buf[5],4) == 0) { /* backup exist ? */
            strcat(buf," ");
            strcat(buf,fname);
            fprintf(stderr,"\n%s\n",buf);
            if (system(buf)==0) {/* copy $erial -> Serial */
                fputs("Restore vxd backup file successful.\n\n",stderr);
                return 1;
            }
        } else {
            fputs("\nvxd backup file not exist.\n\n",stderr);
        }
    }

    if (patch[drv_type].code == NULL) {
        fputs("Error -- unknown driver.\n",stderr);
        exit(1);
    }
    if (drv_type != source) {
        fputs("Error -- unexpected driver.\n",stderr);
        exit(1);
    }
    if (drv_type == 0) {
        /* make backup */
        strcpy(buf,"copy ");
        strcat(buf,fname);
        strcat(buf," ");
        strcat(buf,sysdir);
        strcat(buf,"\\$erial.vxd");
        fprintf(stderr,"\n%s\n",buf);
        if (system(buf)!=0){
            fputs("Error -- Can't backup.\n",stderr);
            exit(1);
        }
        fputs("Backup original to $erial.vxd successful.\n\n",stderr);
    }

    if ((fd = fopen(fname,"r+b")) == NULL) {
        fputs("Error -- Can't open driver file.\n",stderr);
        exit(1);
    }
    if (fseek(fd,adr1,SEEK_SET) != 0) {
        fputs("Error -- Seek.\n",stderr);
        exit(1);
    }
    if (fwrite(patch[dist].code,patch[dist].len,1,fd)!=1) {
        fputs("Error -- write.\n",stderr);
        exit(1);
    }
    if (fseek(fd,(long)TBL_ADR_LEN,SEEK_CUR) != 0) { /* skip table address */
        fputs("Error -- Seek.\n",stderr);
        exit(1);
    }
    if (fwrite(&patch[dist].code[SMC_LEN0+TBL_ADR_LEN],SMC_LEN1,1,fd)!=1) {
        fputs("Error -- write.\n",stderr);
        exit(1);
    }
    if (fseek(fd,adr2,SEEK_SET) != 0) {
        fputs("Error -- Seek.\n",stderr);
        exit(1);
    }
    if (fwrite((dist<2)? x1_tbl:x8_tbl ,24,1,fd)!=1) {
        fputs("Error -- write.\n",stderr);
        exit(1);
    }
    fclose(fd);
    fprintf(stderr,"Apply %s successful.\n\n",patch[dist].name);
    return 2;
}

int check_drv(char opr)
{
    int i;
    long fsize;

    if ((fd = fopen(fname,"rb")) == NULL) {
        if ( (opr == 'P') || (opr == 'R')) {
            fprintf(stderr,"Error -- Can't open driver file '%s'.\n",fname);
            exit(1);
        }
        return -1;
    }
    fseek(fd,0L,SEEK_END);
    fsize = ftell(fd);
    for (i = 0; patch[i].code != NULL; i++) {
        if ((adr1 = search(fsize,patch[i].len,patch[i].code)) != 0L)
            break;
    }
    if (i<2)
        adr2 = search(fsize,24,x1_tbl);
    else if (i<4)
        adr2 = search(fsize,24,x8_tbl);
    if (adr2 == 0L)
        i = 5;  /* old */
    fclose(fd);
    fprintf(stderr,"%s driver detected\n",patch[i].name);
    return i;
}

void chg_autoexec(char opr,char *path)
{
    char *p;
    int replace = 0;
    FILE *ofd;

    if ((ofd = fopen("C:\\AUTOEXEC.TMP","w")) == NULL) {
        fputs("Error -- Can't open 'autoexec.tmp' file.\n",stderr);
        exit(1);
    }
    if (access("C:\\AUTOEXEC.BAT",0) == 0) { /* autoexec.bat is exist */
        sprintf(buf,"copy C:\\AUTOEXEC.BAT C:\\AUTOEXEC.SHD");
        fprintf(stderr,"%s\n",buf);
        if (system(buf) != 0 ) {
            fputs("Error -- Autoexec.bat can't backup !\n",stderr);
            exit(1);
        }
        fprintf(stderr,"Backup AUTOEXEC.BAT successful.\n\n",buf);

        if ((fd = fopen("C:\\AUTOEXEC.BAT","r+")) == NULL) {
            fputs("Error -- Can't open 'autoexec.bat' file.\n",stderr);
            exit(1);
        }
        while (fgets(buf,256,fd)!=NULL) {
            p = buf;
            do {
                if ( (*p == 's') || (*p == 'S') ){
                    if ( (strnicmp(p,"SHSMOD",6) == 0) ||
                        (strnicmp(p,"SMCHSMOD",8) == 0) ) {
                        fprintf(stderr,"SHSMOD will replace line,\n    %s",buf);
                        if (opr == 'P')
                            sprintf(buf,"    %s B H\n",path);
                        else
                            sprintf(buf,"REM %s B H\n",path);
                        fprintf(stderr, " -> %s\n",buf);
                        replace++;
                        break;
                    }
                }
            } while (*p++);
            if (fputs(buf,ofd) < 0) {
                fputs("Error -- write to autoexec.tmp\n",stderr);
                exit(1);
            }
        }
        if (feof(fd) == 0) {
            fputs("Error -- Can't read autoexec.bat\n",stderr);
            exit(1);
        }
        fclose(fd);
    }
    if ( (replace == 0) && (opr == 'P') ) {
        sprintf(buf,"%s B H\n",path);
        fprintf(stderr,"SHSMOD will append line,\n %s\n",buf);
        if (fputs(buf,ofd) < 0) {
            fputs("Error -- apend to autoexec.tmp\n",stderr);
            exit(1);
        }
        replace++;
    }
    fclose(ofd);
    if (replace != 0) {
        sprintf(buf,"copy C:\\AUTOEXEC.TMP C:\\AUTOEXEC.BAT");
        fprintf(stderr,"%s\n",buf);
        if (system(buf) != 0 ) {
            fputs("Error -- Autoexec.bat can't overwrite !\n",stderr);
            exit(1);
        }
        fprintf(stderr,"Overwrite AUTOEXEC.BAT successful.\n",buf);
    }
    unlink("C:\\AUTOEXEC.TMP");
    return;
}

long search(long fsize, int len, unsigned char *code)
{
    int stat;
    long adr;

    stat = 1;
    for(adr = 0x200;adr <fsize - (long)len;) {
        if (fseek(fd,++adr,SEEK_SET) != 0) {
            fputs("Error -- Seek.\n",stderr);
            exit(1);
        }
        for (;adr <fsize - (long)len ; adr++) {
            if ((int)*code == getc(fd)) {
                if (fread(buf,len-1,1,fd) != 1) {
                    fputs("Error -- read.\n",stderr);
                    exit(1);
                }
                if ((stat = memcmp(buf,code+1,len-1)) == 0)
                    return adr;
                break;
            }
        }
    }
    return 0L;
}
#endif
